package debtbundler.domain.bll.solver;

import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;

import debtbundler.domain.entity.Debt;
import debtbundler.domain.entity.Person;
import debtbundler.domain.entity.impl.EntityManager;

/**
 * Tries to minimize number of debts by always picking debtor and creditor pairs
 * with closest match of debt and credit. 
 * 
 * @author Anssi
 *
 */
public class ClosestMatchSolver extends SolverSkeleton {
	private static Logger logger = Logger.getLogger(ClosestMatchSolver.class.getName());
	
	/**
	 * Solves the debts between given persons.

	 * @param persons	persons whose debts to calculate
	 * @throws UnbalancedCostsException
	 * 	if the costs and incomes to the given persons are not balanced within accuracy of 0.005
	 */	
	public ClosestMatchSolver(Collection<Person> persons) throws UnbalancedCostsException {
		solve(persons);
	}	

	@Override
	protected void calculateSolution() {

		while (!(debtors.isEmpty() || creditors.isEmpty())) {
			
			logger.log(Level.FINER, "#Debtors: {0}, #creditors {1}", new Object[]{debtors.size(), creditors.size()});
			
			Person chosenDebtor = null, chosenCreditor = null;
			double minDiff = Double.MAX_VALUE;
			for (Person debtor : debtors) {
				for (Person creditor : creditors) {
					double diff =  Math.abs(creditor.getBalance() + debtor.getBalance());
					if ( diff < minDiff ) {
						minDiff = diff;
						chosenDebtor = debtor;
						chosenCreditor = creditor;
					}
				}
			}
			
			logger.log(Level.FINER, "Matching debtor {0} ({1}) and creditor {2} ({3})", new Object[]{chosenDebtor, chosenDebtor.getBalance(), chosenCreditor, chosenCreditor.getBalance()});
			
			double totalToPay = Math.abs(chosenDebtor.getBalance());
			double totalToGet = Math.abs(chosenCreditor.getBalance());
			Debt debt = EntityManager.createDebt(chosenDebtor, chosenCreditor);
			if (totalToGet > totalToPay) {
				debt.setValue(totalToPay);
				creditors.add(chosenCreditor);
				debtors.remove(chosenDebtor);
			} else if (totalToGet < totalToPay) {
				debt.setValue(totalToGet);
				debtors.add(chosenDebtor);
				creditors.remove(chosenCreditor);
			} else {
				debt.setValue(totalToGet);
				debtors.remove(chosenDebtor);
				creditors.remove(chosenCreditor);
			}
			
			debts.add(debt);
			
			logger.log(Level.FINER, "Created debt {0}", debt);
			
		}
	
		logger.log(Level.FINER, "Debts solved");
	}
	
}
